package pri.damai.xiaowu.common.dubbo.handler;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import pri.damai.xiaowu.common.core.exception.GlobalException;
import pri.damai.xiaowu.common.core.vo.ResultVO;
import pri.damai.xiaowu.common.dubbo.DubboUnifiedHandlerProperties;
import pri.damai.xiaowu.common.dubbo.anno.DubboHandler;

import javax.annotation.PostConstruct;
import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * dubbo调用时统一处理
 * @Desc 统一包装 与 统一异常处理
 * @Author DaMai
 * @Date 2021/5/7 14:23
 * 但行好事，莫问前程。
 */
@ConditionalOnProperty(name = "xiaowu.dubbo.enable", havingValue = "true")
@Component
@Aspect
@Slf4j
public class DubboServiceExceptionHandler {

    @Autowired
    DubboUnifiedHandlerProperties properties;

    @Bean
    @ConditionalOnMissingBean
    public DubboUnifiedHandlerProperties bubboUnifiedHandlerProperties() {
        return new DubboUnifiedHandlerProperties();
    }

    /**
     * 被 {@link DubboHandler} 注解的方法 || 类
     */
    @Pointcut(value = "@annotation(pri.damai.xiaowu.common.dubbo.anno.DubboHandler) " +
            "|| @within(pri.damai.xiaowu.common.dubbo.anno.DubboHandler)")
    private void servicePointcut() {
    }

    /**
     * 任何持有@Transactional注解的方法
     */
    @Pointcut(value = "@annotation(org.springframework.transaction.annotation.Transactional)")
    private void transactionalPointcut() {
    }

    /**
     * 处理切面,包装为 ResultVO
     *
     * @param pjp 处理点
     * @return Object
     */
    @Around("servicePointcut() && !transactionalPointcut() && @annotation(dubboHandler)")
    public Object doAround(ProceedingJoinPoint pjp, DubboHandler dubboHandler) throws Throwable {

        Object[] args = pjp.getArgs();
        Signature signature = pjp.getSignature();
        Object result = null;
        try {
            result = pjp.proceed();
        } catch (Throwable e) {
            processException(pjp, args, e);
            if (!properties.isExceptionUnified()) {
                log.debug(properties.toString());
                throw e;
            }
            // 如果自定义异常，重新包装直接返回
            if (e instanceof GlobalException) {
                GlobalException globalException = (GlobalException) e;
                return ResultVO.error(globalException.getCode(), globalException.getMsg());
            }

            throw e;
        }
        if (dubboHandler.log()) {
            log.info("【Dubbo】远程调用。{}:{},参数:{}；响应:{}", signature.getDeclaringTypeName(),signature.getName(),args, result);
        }
        return result;
    }

    /**
     * 处理异常
     * @apiNote [附加描述]
     * @param joinPoint 切点
     * @param args 参数
     * @param throwable 异常
     * @author DaMai
     * @date 2021/3/24 14:26
     */
    private void processException(final ProceedingJoinPoint joinPoint, final Object[] args, Throwable throwable) {
        String inputParam = "";
        if (args != null && args.length > 0) {
            StringBuilder sb = new StringBuilder();
            for (Object arg : args) {
                sb.append(",");
                sb.append(arg);
            }
            inputParam = sb.toString().substring(1);
        }
        log.warn("方法: {}。入参: {}。错误信息: {}。", joinPoint.toLongString(), inputParam,
                getStackTraceAsString(throwable));
    }

    public static String getStackTraceAsString(Throwable throwable) {
        StringWriter stringWriter = new StringWriter();
        throwable.printStackTrace(new PrintWriter(stringWriter));
        return stringWriter.toString();
    }

    @PostConstruct
    public void successConfig() throws Exception {
        log.info("【Xiao Wu】Dubbo 服务异常处理器配置成功。");
    }
}
